package core;

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JRadioButton;
import javax.swing.JToggleButton;

/**
 * 
 * 		<p><b>PCMView</b> - </p> 
 * 		<p></p> 
 * 
 * 		@author  <br><b>Bartosz 'Dust' Kowalski</b>
 */
class PCMView extends JFrame implements ActionListener
{
	/**
	 * 
	 * 		<p><b>SerializableActionListener</b> - type created to add serialize capabilities to 
	 * 		AWT listeners. All listeners in this class should implment this instead of 
	 * 		ActionListener</p> 
	 * 		<p></p> 
	 * 
	 * 		@author  <br><b>Bartosz 'Dust' Kowalski</b>
	 */
	interface SerializableActionListener extends ActionListener, Serializable
	{	
	}
	
	/**
	 * 
	 * 		<p><b>ComponentState</b> - </p> 
	 * 		<p></p> 
	 * 
	 * 		@author  <br><b>Bartosz 'Dust' Kowalski</b>
	 */
	public enum ComponentState
	{
		POWER_ON_WORKING,
		POWER_ON_STOPPED,
		POWER_OFF
	}

	/**
	 * 
	 * 		<p><b>SettingsChangeListener</b> - </p> 
	 * 		<p></p> 
	 * 
	 * 		@author  <br><b>Bartosz 'Dust' Kowalski</b>
	 */
	public interface SettingsChangeListener extends Serializable
	{
		void onSettingsChanged(PCMSettings settings);
	}
	
	
	
	public static final String				ACTION_DEMODULATION		= "Demodulation";
	public static final String				ACTION_MODULATION		= "Modulation";
	public static final String				ALGORITHM_DPCM			= "DPCM";
	public static final String				ALGORITHM_PCM			= "PCM";
	
	private ComponentState					componentState			= ComponentState.POWER_OFF;
	private List<SettingsChangeListener>	settingsChangeListeners	= new LinkedList<
																		SettingsChangeListener>();
	private JPanel							mainPanel				= new JPanel();
	
	private JRadioButton					modulation				= new JRadioButton(
																				ACTION_MODULATION);
	private JRadioButton					demodulation			= new JRadioButton(
																				ACTION_DEMODULATION);
	private JRadioButton					pcm						= new JRadioButton(
																				ALGORITHM_PCM);
	private JRadioButton					dpcm					= new JRadioButton(
																				ALGORITHM_DPCM);
	
	private ButtonGroup						action					= new ButtonGroup();
	private ButtonGroup						algorithm				= new ButtonGroup();
	private JPanel							actionPanel				= new JPanel();
	private JPanel							algorithmPanel			= new JPanel();
	
	private JProgressBar					workingBar				= new JProgressBar();
	private JToggleButton					onButton				= new JToggleButton();
	private JToggleButton					workButton				= new JToggleButton();
	private JPanel							workingPanel			= new JPanel();
	private JPanel							onOffButtonsPanel		= new JPanel();
	
	private JPanel							paramsPanel				= new JPanel();
	private JPanel							sampleRatePanel			= new JPanel();
	private JPanel							resolutionPanel			= new JPanel();
	private JLabel							sampleRateLabel			= new JLabel("Sample rate [Hz]");
	private JLabel							resolutionLabel			= new JLabel("Resolution [bit]");
	private JComboBox						sampleRateCombo			= new JComboBox();
	private JComboBox						resolutionCombo			= new JComboBox();
	
	private SerializableActionListener 		settingsChangeListener 	= 
			new SerializableActionListener()
			{		
				@Override
				public void actionPerformed(ActionEvent e)
				{			
					runAllSettingsChangeListeners();
				}
			};
	
	
	
	public PCMView()
	{			
		super("Pulse Code Modulator");
					
		modulation.setSelected(true);
		pcm.setSelected(true);
		
		pcm.addActionListener(settingsChangeListener);
		dpcm.addActionListener(settingsChangeListener);
		modulation.addActionListener(settingsChangeListener);
		demodulation.addActionListener(settingsChangeListener);
		
		action.add(modulation);
		action.add(demodulation);
		actionPanel.add(modulation);
		actionPanel.add(demodulation);
		actionPanel.setLayout(new BoxLayout(actionPanel, BoxLayout.Y_AXIS));
		actionPanel.setBorder(BorderFactory.createCompoundBorder(
				BorderFactory.createEmptyBorder(15, 15, 15, 5),
				BorderFactory.createCompoundBorder(
						BorderFactory.createTitledBorder("Action"),
						BorderFactory.createEmptyBorder(5, 30, 5, 5))
				));
		
		algorithm.add(pcm);
		algorithm.add(dpcm);			
		algorithmPanel.setLayout(new BoxLayout(algorithmPanel, BoxLayout.Y_AXIS));
		algorithmPanel.setBorder(BorderFactory.createCompoundBorder(
				BorderFactory.createEmptyBorder(15, 5, 15, 15), 
				BorderFactory.createCompoundBorder(
						BorderFactory.createTitledBorder("Algorithm"),
						BorderFactory.createEmptyBorder(5, 30, 5, 5))
				));
		algorithmPanel.add(pcm);
		algorithmPanel.add(dpcm);
		
		// params section
		
		paramsPanel.setBorder(BorderFactory.createCompoundBorder(
				BorderFactory.createEmptyBorder(5, 15, 15, 5),
				BorderFactory.createCompoundBorder(
						BorderFactory.createTitledBorder("Signal"),
						BorderFactory.createEmptyBorder(5, 5, 5, 5))
				));
		paramsPanel.setLayout(new BoxLayout(paramsPanel, BoxLayout.Y_AXIS));
		paramsPanel.add(sampleRatePanel);
		paramsPanel.add(Box.createRigidArea(new Dimension(0, 5)));
		paramsPanel.add(resolutionPanel);
				
		sampleRatePanel.setLayout(new GridLayout(1, 2, 5, 5));
		sampleRatePanel.add(sampleRateLabel);
		sampleRatePanel.add(sampleRateCombo);
		
		resolutionPanel.setLayout(new GridLayout(1, 2, 5, 5));
		resolutionPanel.add(resolutionLabel);
		resolutionPanel.add(resolutionCombo);		
		
		fillCombos();
		
		sampleRateCombo.addActionListener(settingsChangeListener);
		resolutionCombo.addActionListener(settingsChangeListener);
		// ~params section
				
		// working section
		workingPanel.setLayout(new BoxLayout(workingPanel, BoxLayout.Y_AXIS));
		workingPanel.setBorder(BorderFactory.createCompoundBorder(
				BorderFactory.createEmptyBorder(5, 5, 15, 15), 
				BorderFactory.createCompoundBorder(
						BorderFactory.createTitledBorder("Control"),
						BorderFactory.createEmptyBorder(10, 30, 5, 30))
				));
		workingPanel.add(workingBar);
		workingPanel.add(Box.createRigidArea(new Dimension(0, 5)));			
		workingPanel.add(onOffButtonsPanel);
		
		onOffButtonsPanel.setLayout(new BoxLayout(onOffButtonsPanel, BoxLayout.X_AXIS));
		onOffButtonsPanel.add(onButton);
		onOffButtonsPanel.add(Box.createHorizontalGlue());
		onOffButtonsPanel.add(workButton);
		
		onButton.setText("On");
		onButton.addActionListener(this);
		
		workButton.setText("Run");
		workButton.addActionListener(this);
		workButton.setEnabled(false);
		
		workingBar.setIndeterminate(false);
		workingBar.setStringPainted(true);
		workingBar.setString("disabled");
		// ~working section
		
		mainPanel.setLayout(new GridLayout(2, 2, 0, 0));
		mainPanel.add(actionPanel);
		mainPanel.add(algorithmPanel);
		mainPanel.add(paramsPanel);
		mainPanel.add(workingPanel);
					
		add(mainPanel);			
		pack();	// to resize frame to content
		setResizable(false);
	}
	
	@SuppressWarnings("unchecked")
	private void fillCombos()
	{
		sampleRateCombo.addItem(250.);
		sampleRateCombo.addItem(500.);
		sampleRateCombo.addItem(750.);
		sampleRateCombo.addItem(1000.);
		sampleRateCombo.addItem(1250.);
		sampleRateCombo.addItem(1500.);
		sampleRateCombo.addItem(1750.);
		sampleRateCombo.addItem(2000.);
		sampleRateCombo.addItem(2250.);
		sampleRateCombo.addItem(2500.);
		sampleRateCombo.setSelectedIndex(0);
		
		resolutionCombo.addItem(8);
		resolutionCombo.addItem(16);
		resolutionCombo.addItem(24);
		resolutionCombo.setSelectedIndex(0);
	}
	
	
	/**
	 * 
	 * @return new pack of settings.
	 */
	private PCMSettings createSettings()
	{
		PCMSettings settings = new PCMSettings();
		
		settings.setCurrentAction(modulation.isSelected() ? ACTION_MODULATION : ACTION_DEMODULATION);
		settings.setCurrentAlg(pcm.isSelected() ? ALGORITHM_PCM : ALGORITHM_DPCM);
		settings.setResolution((Integer)resolutionCombo.getItemAt(resolutionCombo.getSelectedIndex()));
		settings.setSampleRate((Double)sampleRateCombo.getItemAt(sampleRateCombo.getSelectedIndex()));
		
		return settings;
	}

	/**
	 * 		@see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	@Override
	public void actionPerformed(ActionEvent e)
	{
		// run when power button is pressed
		if ( onButton.isSelected() && workButton.isSelected() )
		{
			workingBar.setIndeterminate(true);
			workingBar.setString("working...");
			onButton.setText("Off");
			workButton.setText("Stop");
			workButton.setEnabled(true);
			
			setComponentState(ComponentState.POWER_ON_WORKING);
						
			runAllSettingsChangeListeners();
		}
		else if ( onButton.isSelected() && !workButton.isSelected() )
		{
			workingBar.setIndeterminate(false);
			workingBar.setString("powered on");
			onButton.setText("Off");
			workButton.setText("Run");
			workButton.setEnabled(true);
			
			setComponentState(ComponentState.POWER_ON_STOPPED);
		}
		else
		{
			workingBar.setIndeterminate(false);
			workingBar.setString("powered off");
			onButton.setText("On");
			workButton.setText("Run");
			workButton.setEnabled(false);
			workButton.setSelected(false);
			
			setComponentState(ComponentState.POWER_OFF);
		}
	}

	public void setComponentState(ComponentState componentState)
	{
		this.componentState = componentState;
	}

	public ComponentState getComponentState()
	{
		return componentState;
	}
	
	public void addSettingsChangeListener(SettingsChangeListener listener)
	{
		settingsChangeListeners.add(listener);
	}
	
	public void removeSettingsChangeListener(SettingsChangeListener listener)
	{
		settingsChangeListeners.remove(listener);
	}
	
	public void removeAllSettingsChangeListeners()
	{
		settingsChangeListeners.clear();
	}
	
	public void runAllSettingsChangeListeners()
	{
		PCMSettings settings = createSettings();
		
		for ( SettingsChangeListener l : settingsChangeListeners )
			l.onSettingsChanged(settings);
	}
}